package cn.go.processor.processor;


import com.google.auto.service.AutoService;


import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;


import cn.go.buster.ThreadMode;
import cn.go.buster.annotation.GoBus;
import cn.go.buster.event.EventCard;
import jdk.nashorn.internal.runtime.JSONFunctions;


/**
 * @author logcat
 */
@AutoService(Processor.class)
public class GoBusProcessor extends AbstractProcessor {


    private Filer filter;

    private Messager messager;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        filter = processingEnvironment.getFiler();
        this.messager = processingEnvironment.getMessager();
    }


    @Override
    public SourceVersion getSupportedSourceVersion() {
        return processingEnv.getSourceVersion();
    }


    /**
     * What are the annotations to be processed by the annotation processor
     *
     * @return
     */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> types = new HashSet<>();
        types.add(GoBus.class.getCanonicalName());
        return types;
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {

        Set<? extends Element> elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(GoBus.class);
        Map<String, EventCard> eventMap = new HashMap<>();
        for (Element element : elementsAnnotatedWith) {
            if (element.getKind() != ElementKind.METHOD) {
                continue;
            }

            ExecutableElement methodElement = (ExecutableElement) element;

            messager.printMessage(Diagnostic.Kind.WARNING, "processor start-------"
                    + methodElement.getSimpleName());

            TypeElement typeElement = (TypeElement) methodElement.getEnclosingElement();
            String tag = methodElement.getAnnotation(GoBus.class).tag();
            ThreadMode threadMode = methodElement.getAnnotation(GoBus.class).threadMode();
            String className = typeElement.getQualifiedName().toString();

            List<? extends VariableElement> parameters = methodElement.getParameters();
            List<String> paramTypeList = new ArrayList<>();
            StringBuilder paramBuilder = new StringBuilder();
            for (VariableElement parameter : parameters) {
                messager.printMessage(Diagnostic.Kind.WARNING, "Method:" + methodElement.getSimpleName() + "   params: " + parameter.asType().toString());
                paramTypeList.add(parameter.asType().toString());
                paramBuilder.append(parameter.asType().toString()).append(",");
            }
            if (paramBuilder.length() > 1) {
                paramBuilder.deleteCharAt(paramBuilder.length() - 1);
            }
            EventCard eventCard = new EventCard(tag, className, null, methodElement.getSimpleName().toString(), null, paramBuilder.toString(), threadMode);
            StringBuilder keyBuilder = new StringBuilder();
            keyBuilder.append(tag).append("_").append(className);
            eventMap.put(keyBuilder.toString(), eventCard);
        }
        if (eventMap.size() > 0) {
            Writer writer = null;

            String warehouseName = "EventContainer_" + System.currentTimeMillis();
            try {
                JavaFileObject sourceFile = filter.createSourceFile("cn.go.buster.launcher." + warehouseName);
                writer = sourceFile.openWriter();
                writer.write("package cn.go.buster.launcher;\n" +
                        "\n" +
                        "import cn.go.buster.launcher.api.EventCenter;\n" +
                        "import cn.go.buster.launcher.api.BaseGoBuster;\n" +
                        "import static cn.go.buster.ThreadMode.MAIN;\n" +
                        "import static cn.go.buster.ThreadMode.BACKGROUND;\n" +
                        "import cn.go.buster.event.EventCard;\n" +
                        "\n" +
                        "public class " + warehouseName + " extends BaseGoBuster {\n" + "\n" +
                        "    @Override\n" +
                        "    protected void put(Object node) {\n");

                Iterator<String> iterator = eventMap.keySet().iterator();
                while (iterator.hasNext()) {
                    String tag = iterator.next();
                    String eventClassName = eventMap.get(tag).getEventClassName();
                    String methodName = eventMap.get(tag).getMethodName();
                    String parameters = eventMap.get(tag).getParamTypes();
                    ThreadMode threadMode = eventMap.get(tag).getThreadMode();
                    writer.write("        EventCenter.getInstance().put(new EventCard(\"" + tag +
                            "\" , \"" + eventClassName +
                            "\" , null , \"" + methodName +
                            "\" , node , \"" + parameters +
                            "\" , " + threadMode + "));\n");
                }


                writer.write("    }\n" +
                        "\n" +
                        "    @Override\n" +
                        "    protected void remove(Object node) {\n");


                Iterator<String> iterator2 = eventMap.keySet().iterator();
                while (iterator2.hasNext()) {
                    String tag = iterator2.next();
                    String eventClassName = eventMap.get(tag).getEventClassName();
                    String parameters = eventMap.get(tag).getParamTypes();
                    ThreadMode threadMode = eventMap.get(tag).getThreadMode();

                    writer.write("        EventCenter.getInstance().remove(new EventCard(\"" + tag +
                            "\" , \"" + eventClassName +
                            "\" , null , null , node , \"" + parameters +
                            "\" , " + threadMode + "));\n");
                }


                writer.write("    }\n" +
                        "}\n");

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return false;
    }
}
